﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using SunXNA.RainbowXNA.RenderObject;
using SunXNA.RainbowXNA.Material;

namespace SunXNA.RainbowXNA.Batch
{
    class CBatchManager
    {
        #region Public Types
        #endregion

        #region Private Member Variables

        //
        //  Batch Renderer for each material type
        //
        //private CMaterialBatchRenderer[] m_batches;
        private CMaterialManager m_materialManager;

        private Dictionary<UInt32, CMaterialBatchRenderer[]> m_batches;

        #endregion

        #region Protected Member Variables
        #endregion

        #region Puplic Member Properties
        #endregion

        #region Public Member Functions

        public CBatchManager()
        {
            //m_batches = new CMaterialBatchRenderer[(int)MaterialType.NumberOf];
            //m_materialManager = null;

            //for (MaterialType type = MaterialType.Flat; type < MaterialType.NumberOf; ++type)
            //{
            //    m_batches[(int)type] = new CMaterialBatchRenderer();
            //}

            m_batches = new Dictionary<uint, CMaterialBatchRenderer[]>();
        }

        public void Initialise(CMaterialManager materialManager)
        {
            m_materialManager = materialManager;

            m_batches[0] = new CMaterialBatchRenderer[(int)MaterialType.NumberOf];

            for (MaterialType type = MaterialType.Flat; type < MaterialType.NumberOf; ++type)
            {
                if (type != MaterialType.SkinnedLambert && type != MaterialType.Terrain)
                {
                    m_batches[0][(int)type] = new CMaterialBatchRenderer();
                }
                else if (type != MaterialType.Terrain)
                {
                    m_batches[0][(int)type] = new CAnimatedMaterialBatchRenderer();
                }
                else
                {
                    m_batches[0][(int)type] = new CTerrainMaterialBatchRenderer();
                }

                m_batches[0][(int)type].Initialise(m_materialManager.Effects[(int)type]);
            }
        }

        public void Add(CRainbowXNA.CRenderInstance instance)
        {
            //m_batches[(int)instance.Material.Type].Add(instance);

            CMaterialBatchRenderer[] materialBatch = null;
            UInt32 textureID = (instance.Material.TextureHandle != null) ? instance.Material.TextureHandle.UID
                                                                         : 0;   //null UID

            if (!m_batches.TryGetValue(textureID, out materialBatch))
            {
                m_batches[textureID] = new CMaterialBatchRenderer[(int)MaterialType.NumberOf];
                materialBatch = m_batches[textureID];

                for (MaterialType type = MaterialType.Flat; type < MaterialType.NumberOf; ++type)
                {
                    if (type != MaterialType.SkinnedLambert && type != MaterialType.Terrain)
                    {
                        materialBatch[(int)type] = new CMaterialBatchRenderer();
                    }
                    else if (type != MaterialType.Terrain)
                    {
                        materialBatch[(int)type] = new CAnimatedMaterialBatchRenderer();
                    }
                    else
                    {
                        materialBatch[(int)type] = new CTerrainMaterialBatchRenderer();
                    }

                    materialBatch[(int)type].Initialise(m_materialManager.Effects[(int)type]);
                }
            }

            materialBatch[(int)instance.Material.Type].Add(instance);
        }

        public void Clear()
        {
            //foreach (CMaterialBatchRenderer materialBatch in m_batches)
            //{
            //    materialBatch.Clear();
            //}

            foreach (KeyValuePair<UInt32,CMaterialBatchRenderer[]> materialBatchArrayPair in m_batches)
            {
                foreach (CMaterialBatchRenderer materialBatch in materialBatchArrayPair.Value)
                {
                    materialBatch.Clear();
                }
            }

            m_batches.Clear();
        }

        public void Draw(ref Matrix m4View, ref Matrix m4Projection, ref GraphicsDevice device, ref Dictionary<UInt32, IBaseRenderObject> renderObjects, ref Dictionary<UInt32,Texture2D> textures)
        {
            //foreach (CMaterialBatchRenderer materialBatch in m_batches)
            //{
            //    materialBatch.Draw(ref m4View, ref m4Projection, ref device, ref renderObjects, ref textures);
            //}

            foreach (KeyValuePair<UInt32, CMaterialBatchRenderer[]> materialBatchArrayPair in m_batches)
            {
                foreach (CMaterialBatchRenderer materialBatch in materialBatchArrayPair.Value)
                {
                    materialBatch.Draw(ref m4View, ref m4Projection, ref device, ref renderObjects, ref textures, materialBatchArrayPair.Key);
                }
            }
        }

        #endregion

        #region Protected Member Functions
        #endregion

        #region Private Member Functions
        #endregion
    }
}
